package com.lizi.idgenerator.service.impl;
import java.util.Date;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lizi.idgenerator.dto.idcreator.IdCreatorAddModel;
import com.lizi.idgenerator.dto.idcreator.IdCreatorResultModel;
import com.lizi.idgenerator.dto.ResultModel;
import com.lizi.idgenerator.entity.IdCreator;
import com.lizi.idgenerator.entity.IdCreatorRecord;
import com.lizi.idgenerator.mapper.IdCreatorMapper;
import com.lizi.idgenerator.mapper.IdCreatorRecordMapper;
import com.lizi.idgenerator.service.IdCreatorService;
import com.lizi.idgenerator.utils.ModelValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.hutool.core.util.StrUtil;
/**
 * @author kingman
 * @since 2020-12-15 13:50
 */
@Service
public class IdCreatorServiceImpl implements IdCreatorService {

    private static Logger logger = LoggerFactory.getLogger(IdCreatorServiceImpl.class);

    @Autowired
    private IdCreatorMapper idCreatorMapper;
    @Autowired
    private IdCreatorRecordMapper idCreatorRecordMapper;


    /**
     * 创建Id生成器
     * @param idCreatorAddModel
     * @return
     */
    @Override
    public ResultModel<Boolean> add(IdCreatorAddModel idCreatorAddModel)
    {
        ModelValidator.validate(idCreatorAddModel);

        IdCreator idCreator = getByBusinessId(idCreatorAddModel.getBusinessId());
        if(idCreator!=null){
            return ResultModel.failResult(-2001,"已经存在相同的生成器Id，无法添加");
        }
        IdCreator addModel = new IdCreator();
        addModel.setBusinessId(idCreatorAddModel.getBusinessId());
        addModel.setCurrentValue(idCreatorAddModel.getCurrentValue());
        addModel.setStepValue(idCreatorAddModel.getStepValue());
        addModel.setDescription(idCreatorAddModel.getDescription());
        addModel.setCreateTime(new Date());
        addModel.setUpdateTime(new Date());
        idCreatorMapper.insert(addModel);

        return ResultModel.successResult(true);
    }

    /**
     * 通过业务Id查询
     * @param businessId
     * @return
     */
    @Override
    public IdCreator getByBusinessId(String businessId){
        if(StrUtil.isEmpty(businessId)){
            return null;
        }

        LambdaQueryWrapper<IdCreator> queryWrapper = new QueryWrapper<IdCreator>().lambda();
        queryWrapper.eq(IdCreator::getBusinessId,businessId);
        IdCreator idCreator = idCreatorMapper.selectOne(queryWrapper);
        return idCreator;
    }

    /**
     * 生成Id
     * <p>事务：SERIALIZABLE以下的等级，可重复读，这个事务等级保证了update的一致性，即更新后的数据是准确的，但在并发时，查询的数据是不保证一定是及时的。
     * 这样有个问题，就是idCreator.getCurrentValue()为旧值，还是事务进入后，查询时的数值，有可能不是最新的。所以如果要resultModel.setCurrentValue，
     * 就要拿到最后更新的值-步长 才能得到更新时的那个值。此事务执行该方法平均1.5秒
     * </p>
     * <p>事务：SERIALIZABLE，序列化,代价最高最可靠的隔离级别,如果用该事务等级，就不会出现重复读的问题，但带来较差的性能。
     * 此事务处理该方法2.5秒以上
     * </p>
     * @param businessId
     * @return
     */
//    @Transactional(isolation = Isolation.DEFAULT)
    @Override
    @Transactional
    public ResultModel<IdCreatorResultModel> generateId(String businessId){
        if(StrUtil.isEmpty(businessId))
        {
            return ResultModel.failResult(-2001,"生成器Id不能为空");
        }
        IdCreator idCreator = getByBusinessId(businessId);
        if(idCreator==null){
            return ResultModel.failResult(-2001,String.format("未找到业务Id（%s）的生成器",businessId));
        }
        idCreatorMapper.updateCurrentValue(idCreator.getId());
        IdCreator afterCreateModel = getByBusinessId(businessId);

        IdCreatorResultModel resultModel = new IdCreatorResultModel();
        resultModel.setId(idCreator.getId());
        resultModel.setBusinessId(idCreator.getBusinessId());

        //resultModel.setCurrentValue(idCreator.getCurrentValue()); //事务等级为：SERIALIZABLE以下等级时，这里的赋值是有问题的。

        resultModel.setCurrentValue(afterCreateModel.getCurrentValue()-idCreator.getStepValue());
        resultModel.setCreatedValue(afterCreateModel.getCurrentValue());

        IdCreatorRecord idCreatorRecord = new IdCreatorRecord();
        idCreatorRecord.setCreatorId(idCreator.getId());
        idCreatorRecord.setCurrentValue(afterCreateModel.getCurrentValue()-idCreator.getStepValue());
        idCreatorRecord.setStepValue(idCreator.getStepValue());
        idCreatorRecord.setCreatedValue(afterCreateModel.getCurrentValue());
        idCreatorRecord.setCreateTime(new Date());
        idCreatorRecordMapper.insert(idCreatorRecord);

        return ResultModel.successResult(resultModel);

    }



}
